"""决策树算法是一种既可以用于分类，也可以用于回归的算法。

决策树回归是通过对输入特征的不断划分来建立一棵决策树，每一步划分都基于当前数据集的最优划分特征。
它的目标是最小化总体误差或最大化预测精度，其构建通常采用自上而下的贪心搜索方式，通过比较不同划分标准来选择最优划分。

决策树回归广泛应用于各种回归问题，如预测房价、股票价格、客户流失等。

1. 算法概述
决策树相关的诸多算法之中，有一种CART算法，全称是 classification and regression tree（分类与回归树）。
顾名思义，这个算法既可以用来分类，也可以用来回归，本篇主要介绍其在回归问题上的应用。

决策树算法的核心在于生成一棵决策树过程中，如何划分各个特征到树的不同分支上去。
CART算法是根据基尼系数（Gini）来划分特征的，每次选择基尼系数最小的特征作为最优切分点。

其中基尼系数的计算方法：
gini(p)=∑ni=1pi(1−pi)=1−∑ni=1p2i
2. 创建样本数据
这次的回归样本数据，我们用 scikit-learn 自带的玩具数据集中的糖尿病数据集。
关于玩具数据集的内容，可以参考：TODO"""

from sklearn.datasets import load_diabetes
from sklearn import preprocessing as pp
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeRegressor
from sklearn import metrics

# 糖尿病数据集
diabetes = load_diabetes()
X = diabetes.data
y = diabetes.target
"""这个数据集中大约有400多条数据。

3. 模型训练
训练之前，为了减少算法误差，先对数据进行标准化处理。"""


# 数据标准化
X = pp.scale(X)
y = pp.scale(y)
# 接下来分割训练集和测试集。


# 分割训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1)
# 然后用scikit-learn中的DecisionTreeRegressor模型来训练：


# 定义决策树回归模型
reg = DecisionTreeRegressor(max_depth=2)

# 训练模型
reg.fit(X_train, y_train)

# 在测试集上进行预测
y_pred = reg.predict(X_test)
"""DecisionTreeRegressor的主要参数包括：

criterion：用于衡量节点划分质量的指标。可以选择的值有'mse'（均方误差）或'mae'（平均绝对误差）。默认值为'mse'，适用于大多数情况。
splitter：用于决定节点如何进行划分的策略。可以选择的值有'best'（选择最佳划分）或'random'（随机划分）。默认值为'best'。
max_depth：决策树的最大深度。默认值为None，表示不限制最大深度。增加最大深度有助于更好地拟合训练数据，但可能导致过拟合。
random_state：用于设置随机数生成器的种子。默认值为None，表示使用随机数生成器。
ccp_alpha：用于控制正则化强度的参数。默认值为None，表示不进行正则化。
max_samples：用于控制每个节点最少需要多少样本才能进行分裂。默认值为None，表示使用整个数据集。
min_samples_split：用于控制每个节点最少需要多少样本才能进行分裂。默认值为2，表示每个节点至少需要2个样本才能进行分裂。
min_samples_leaf：用于控制每个叶子节点最少需要多少样本才能停止分裂。默认值为1，表示每个叶子节点至少需要1个样本才能停止分裂。
min_weight_fraction_leaf：用于控制每个叶子节点最少需要多少样本的权重才能停止分裂。默认值为0.0，表示每个叶子节点至少需要0个样本的权重才能停止分裂。
max_features：用于控制每个节点最多需要考虑多少个特征进行分裂。默认值为None，表示使用所有特征。
max_leaf_nodes：用于控制决策树最多有多少个叶子节点。默认值为None，表示不限制叶子节点的数量。
min_impurity_decrease：用于控制每个节点最少需要减少多少不纯度才能进行分裂。默认值为0.0，表示每个节点至少需要减少0个不纯度才能进行分裂。
min_impurity_split：用于控制每个叶子节点最少需要减少多少不纯度才能停止分裂。默认值为None，表示使用min_impurity_decrease参数。
class_weight：用于设置类别权重的字典或方法。默认值为None，表示使用均匀权重。
最后验证模型的训练效果："""


# 在测试集上进行预测
y_pred = reg.predict(X_test)

mse, r2, m_error = 0.0, 0.0, 0.0
y_pred = reg.predict(X_test)
mse = metrics.mean_squared_error(y_test, y_pred)
r2 = metrics.r2_score(y_test, y_pred)
m_error = metrics.median_absolute_error(y_test, y_pred)

print("均方误差：{}".format(mse))
print("复相关系数：{}".format(r2))
print("中位数绝对误差：{}".format(m_error))

"""# 运行结果
均方误差：0.5973573097746598
复相关系数：0.5153160857515913
中位数绝对误差：0.5496418600646286
从预测的误差来看，训练的效果还不错。
这里用DecisionTreeRegressor训练模型时使用了参数max_depth=2，
我从max_depth=1逐个尝试到了max_depth=10，发现max_depth=2时误差最小。

4. 总结
决策树回归具有直观、易于理解、易于实现等优点。
生成的决策树可以直观地展示出输入特征与输出结果之间的关系，因此对于非专业人士来说也易于理解。
此外，决策树回归算法相对简单，易于实现，且对数据的预处理要求较低。

然而，决策树回归也存在一些缺点。
首先，它容易过拟合训练数据，特别是当训练数据量较小时；
其次，决策树的性能受划分标准选择的影响较大，不同的划分标准可能会导致生成的决策树性能差异较大；
此外，决策树回归在处理大规模数据时可能会比较耗时，因为需要遍历整个数据集进行训练和预测。"""